/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.northwestern.cbits.activitydetector.app; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks; import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener; import com.google.android.gms.location.ActivityRecognitionClient; import android.app.Activity; import android.app.Dialog; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; import android.os.Bundle; import android.util.Log; /** * Class for connecting to Location Services and activity recognition updates. * <b> * Note: Clients must ensure that Google Play services is available before requesting updates. * </b> Use GooglePlayServicesUtil.isGooglePlayServicesAvailable() to check. * * * To use a DetectionRequester, instantiate it and call requestUpdates(). Everything else is done * automatically. * */ public class DetectionRequester implements ConnectionCallbacks, OnConnectionFailedListener { // Storage for a context from the calling client private Context mContext; // Stores the PendingIntent used to send activity recognition events back to the app private PendingIntent mActivityRecognitionPendingIntent; // Stores the current instantiation of the activity recognition client private ActivityRecognitionClient mActivityRecognitionClient; public DetectionRequester(Context context) { // Save the context mContext = context; // Initialize the globals to null mActivityRecognitionPendingIntent = null; mActivityRecognitionClient = null; } /** * Returns the current PendingIntent to the caller. * * @return The PendingIntent used to request activity recognition updates */ public PendingIntent getRequestPendingIntent() { return mActivityRecognitionPendingIntent; } /** * Sets the PendingIntent used to make activity recognition update requests * @param intent The PendingIntent */ public void setRequestPendingIntent(PendingIntent intent) { mActivityRecognitionPendingIntent = intent; } /** * Start the activity recognition update request process by * getting a connection. */ public void requestUpdates() { requestConnection(); } /** * Make the actual update request. This is called from onConnected(). */ private void continueRequestActivityUpdates() { /* * Request updates, using the default detection interval. * The PendingIntent sends updates to ActivityRecognitionIntentService */ getActivityRecognitionClient().requestActivityUpdates( ActivityUtils.DETECTION_INTERVAL_MILLISECONDS, createRequestPendingIntent()); // Disconnect the client requestDisconnection(); } /** * Request a connection to Location Services. This call returns immediately, * but the request is not complete until onConnected() or onConnectionFailure() is called. */ private void requestConnection() { getActivityRecognitionClient().connect(); } /** * Get the current activity recognition client, or create a new one if necessary. * This method facilitates multiple requests for a client, even if a previous * request wasn't finished. Since only one client object exists while a connection * is underway, no memory leaks occur. * * @return An ActivityRecognitionClient object */ private ActivityRecognitionClient getActivityRecognitionClient() { if (mActivityRecognitionClient == null) { mActivityRecognitionClient = new ActivityRecognitionClient(mContext, this, this); } return mActivityRecognitionClient; } /** * Get the current activity recognition client and disconnect from Location Services */ private void requestDisconnection() { getActivityRecognitionClient().disconnect(); } /* * Called by Location Services once the activity recognition client is connected. * * Continue by requesting activity updates. */ @Override public void onConnected(Bundle arg0) { // If debugging, log the connection Log.d(ActivityUtils.APPTAG, mContext.getString(R.string.connected)); // Continue the process of requesting activity recognition updates continueRequestActivityUpdates(); } /* * Called by Location Services once the activity recognition client is disconnected. */ @Override public void onDisconnected() { // In debug mode, log the disconnection Log.d(ActivityUtils.APPTAG, mContext.getString(R.string.disconnected)); // Destroy the current activity recognition client mActivityRecognitionClient = null; } /** * Get a PendingIntent to send with the request to get activity recognition updates. Location * Services issues the Intent inside this PendingIntent whenever a activity recognition update * occurs. * * @return A PendingIntent for the IntentService that handles activity recognition updates. */ private PendingIntent createRequestPendingIntent() { // If the PendingIntent already exists if (null != getRequestPendingIntent()) { // Return the existing intent return mActivityRecognitionPendingIntent; // If no PendingIntent exists } else { // Create an Intent pointing to the IntentService Intent intent = new Intent(mContext, ActivityRecognitionIntentService.class); /* * Return a PendingIntent to start the IntentService. * Always create a PendingIntent sent to Location Services * with FLAG_UPDATE_CURRENT, so that sending the PendingIntent * again updates the original. Otherwise, Location Services * can't match the PendingIntent to requests made with it. */ PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); setRequestPendingIntent(pendingIntent); return pendingIntent; } } /* * Implementation of OnConnectionFailedListener.onConnectionFailed * If a connection or disconnection request fails, report the error * connectionResult is passed in from Location Services */ @Override public void onConnectionFailed(ConnectionResult connectionResult) { /* * Google Play services can resolve some errors it detects. * If the error has a resolution, try sending an Intent to * start a Google Play services activity that can resolve * error. */ if (connectionResult.hasResolution()) { try { connectionResult.startResolutionForResult((Activity) mContext, ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST); /* * Thrown if Google Play services canceled the original * PendingIntent */ } catch (SendIntentException e) { // display an error or log it here. } /* * If no resolution is available, display Google * Play service error dialog. This may direct the * user to Google Play Store if Google Play services * is out of date. */ } else { Dialog dialog = GooglePlayServicesUtil.getErrorDialog( connectionResult.getErrorCode(), (Activity) mContext, ActivityUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST); if (dialog != null) { dialog.show(); } } } }